
import { _decorator, Component, Node, resources, Asset, assetManager, director, SceneAsset, AssetManager, Prefab, AudioClip, JsonAsset, debug, Scene } from 'cc';
import Common from '../../utils/Common';
import eventUtil from './eventUtil';
import { audioManager } from './audioManager';
import { poolManager } from './poolManager';
import { EventConst } from '../../const/Const';
import { Debug } from '../../utils/Debug';
const { ccclass, property } = _decorator;

@ccclass('ResourceModule')
export class resourceUtil {
    /**
   * 加载resources下的cc.SpriteFrame, cc.AnimationClip, cc.Prefab
   * 不带扩展名
   * @method loadAsset
   * @param {String} url resources下路径
   * @param {typeof cc.Asset} assetType cc.SpriteFrame, cc.AnimationClip, cc.Prefab..
   * @param {Function} [callback] (err:Error,asset:cc.Asset)
   * @param {typeof cc.Asset} callback.asset cc.SpriteFrame, cc.AnimationClip, cc.Prefab..
   */
    public static loadAsset(url: string, assetType: any, callback: (err: any, asset: any) => void) {
        if (url.indexOf("http") != -1) {
            assetManager.loadRemote(url, (err, asset) => {
                if (callback) {
                    callback(err, asset);
                }
            })
        }
        else
            resources.load(url, JsonAsset, (err: any, asset: any) => {
                if (callback) {
                    callback(err, asset);
                }
            })
    }

    /**
    * 加载resources目录下某个目录下的指定类型的资源(用于预加载整个目录资源)
    * @param {string} dir resources下的目录
    * @param {typeof cc.Asset} type 
    * @param {Function} progressCallback (precent:number)
    * @param {Function} completeCallback (err:Error,reses:Asset[])
    */
    public static loadAssetDir(dir: string, type: typeof Asset, progressCallback: (precent: number) => void, completeCallback: (error: any, assets: Array<any>) => void) {

        resources.loadDir(dir, type, (completedCount, totalCount, item) => {
            let precent = completedCount / totalCount * 100;
            precent = Math.ceil(precent);
            if (progressCallback) {
                progressCallback(precent);
            }
        }, (err, res) => {
            if (completeCallback) {
                completeCallback(err, res);
            }
        })
    }

    /**
     * 预加载场景
     * @param sceneName 
     * @param onProgress 
     * @param onLoaded 
     */
    public static preloadScene(sceneName: string, onProgress?: (completedCount: number, totalCount: number, item: any) => void, onLoaded?: (error: null | Error, sceneAsset?: SceneAsset) => void, bundleKey: string = "") {
        if (bundleKey == "") {
            director.preloadScene(sceneName, onProgress, onLoaded)
        } else {
            this.loadBundle(bundleKey, (err, bundle: AssetManager.Bundle) => {
                bundle.preloadScene(sceneName, onProgress, onLoaded);
            })
        }
    }

    /**
     * 加载场景
     * @param sceneName 
     */
    public static loadScene(sceneName: string, callback?: (error: null | Error, scene?: Scene) => void, bundleKey: string = "") {
        if (bundleKey == "") {
            director.loadScene(sceneName, callback);
        } else {
            this.loadBundle(bundleKey, (err, bundle: AssetManager.Bundle) => {
                bundle.loadScene(sceneName, (err, sceneAsset) => director.runScene(sceneAsset, null, callback));
            });
        }
    }



    public static loadBundle(nameOrUrl: string, completed: (err: any, bundle: AssetManager.Bundle) => void, onFileProgress?: (progress: number) => void) {
        assetManager.loadBundle(nameOrUrl, {
            onFileProgress: (obj: any) => {
                let precent = obj.progress;
                precent = Math.ceil(precent);
                Debug.log(`${nameOrUrl}加载进度:${obj.progress}%  `, obj.progress);
                if (onFileProgress)
                    onFileProgress(obj.progress);
            }
        }, (err, bundle: AssetManager.Bundle) => {
            // Debug.log(`${nameOrUrl}加载完成`);
            completed(err, bundle)
        });
    }


    public static preloadBundleDir(bundleName: string, type: typeof Asset, assetName: string) {
        this.loadBundle(bundleName, (err, bundle: AssetManager.Bundle) => {
            bundle.preloadDir(assetName, type)
        })
    }

    /**
     * 批量加载资源
     * @param bundleName 
     * @param type 
     * @param dirName 
     * @param progressCallback 
     * @param completeCallback 
     */
    public static loadBundleDir(bundleName: string, type: typeof Asset, dirName: string, progressCallback: (precent: number) => void, completeCallback: (error: any, assets: Array<Asset>) => void) {
        this.loadBundle(bundleName, (err, bundle: AssetManager.Bundle) => {
            bundle.loadDir(dirName, type, (completedCount, totalCount, item) => {
                let precent = completedCount / totalCount * 100;
                precent = Math.ceil(precent);
                if (progressCallback) {
                    progressCallback(precent);
                }
            }, (err, res) => {
                if (completeCallback) {
                    completeCallback(err, res);
                }
            })
        })
    }

    public static loadBundleRes<T extends Asset>(bundleName: string, assetName: string, callback: (err: any, asset: T) => void) {
        this.loadBundle(bundleName, (err, bundle: AssetManager.Bundle) => {
            bundle.load<T>(assetName, (err, res: T) => {
                callback(err, res)
            });
        })
    }


    public static preloadBundleRes(bundleName: string, assetName: string, callback: () => void) {
        this.loadBundle(bundleName, (err, bundle: AssetManager.Bundle) => {
            bundle.preload(assetName, callback);
        })
    }




    /**
     * 加载子包的预制体
     * @param nameOrUrl 子包名称
     * @param onProgress 进度
     * @param onComplete 加载完成
     * @param entityDir  entity目录
     * @param showLoading 显示加载进度
     */
    public static loadSubEntity(nameOrUrl: string, onProgress: (precent: any) => void, onComplete: () => void, entityDir: string = "", showLoading: boolean = true) {
        if (showLoading)
            eventUtil.sendEventImmediately(EventConst.SHOW_TRANSITION, null);
        this.loadBundle(nameOrUrl, (err, bundle: AssetManager.Bundle) => {
            if (bundle == null) {
                Debug.warn("bundle 是空 ", nameOrUrl);
                debugger;
                return;
            }
            bundle.loadDir(entityDir, Prefab, (finish: number, total: number) => {
                let precent = finish / total * 100;
                if (Common.isFunction(onProgress))
                    onProgress(precent)
                // Debug.log('loadSubEntity onProgress ', nameOrUrl, precent)
            }, (err, prefabs: Array<Prefab>) => {
                if (showLoading)
                    eventUtil.sendEventImmediately(EventConst.HIDE_TRANSITION, null)
                if (err) {
                    Debug.warn('loadSubEntity err ', nameOrUrl, err)
                    onComplete();
                    return;
                }
                // Debug.log('loadSubEntity prefabs ', nameOrUrl, prefabs);
                prefabs.forEach(prefab => {
                    poolManager.addPrefab(prefab);
                })
                if (Common.isFunction(onComplete))
                    onComplete();
            })
        })
    }


    /**
     * 加载子包的Form
     * @param nameOrUrl 子包名称
     * @param onProgress 进度
     * @param onComplete 加载完成
     * @param entityDir  entity目录
     * @param showLoading 显示加载进度
     */
    public static loadSubForm(nameOrUrl: string, onProgress: (precent: any) => void, onComplete: () => void, uiDir: string = "form", showLoading: boolean = true) {
        if (showLoading)
            eventUtil.sendEventImmediately(EventConst.SHOW_TRANSITION, null);
        this.loadBundle(nameOrUrl, (err, bundle: AssetManager.Bundle) => {
            bundle.loadDir(uiDir, Prefab, (finish: number, total: number) => {
                let precent = finish / total * 100;
                if (Common.isFunction(onProgress))
                    onProgress(precent)
            }, (err: any, prefabs: Array<Prefab>) => {
                if (showLoading)
                    eventUtil.sendEventImmediately(EventConst.HIDE_TRANSITION, null)
                if (err) {
                    Debug.warn('loadSubForm err ', err)
                    onComplete();
                    return;
                }
                prefabs.forEach(prefab => {
                    poolManager.addPrefab(prefab);
                })
                if (Common.isFunction(onComplete))
                    onComplete();
            })
        })
    }




    /**
     * 加载音频
     * @param nameOrUrl 
     * @param onProgress 
     * @param onComplete 
     * @param soundDir 
     * @param showLoading 
     */
    public static loadBundleSound(nameOrUrl: string, onProgress: (precent: number) => void, onComplete: () => void, soundDir: string = "sound", showLoading: boolean = true) {
        if (showLoading)
            eventUtil.sendEventImmediately(EventConst.SHOW_TRANSITION, null);
        this.loadBundle(nameOrUrl, (err, bundle: AssetManager.Bundle) => {
            bundle.loadDir(soundDir, AudioClip, (finish: number, total: number) => {
                let precent = finish / total * 100;
                if (Common.isFunction(onProgress))
                    onProgress(precent)
            }, (err, audios: Array<AudioClip>) => {
                if (showLoading)
                    eventUtil.sendEventImmediately(EventConst.HIDE_TRANSITION, null)
                if (err) {
                    Debug.warn('loadSound err ', err)
                    onComplete();
                    return;
                }
                audios.forEach(audio => {
                    audioManager.addAudio(audio);
                })
                if (Common.isFunction(onComplete))
                    onComplete();
            })
        })
    }
}

/**
 * [1] Class member could be defined like this.
 * [2] Use `property` decorator if your want the member to be serializable.
 * [3] Your initialization goes here.
 * [4] Your update function goes here.
 *
 * Learn more about scripting: https://docs.cocos.com/creator/3.0/manual/en/scripting/
 * Learn more about CCClass: https://docs.cocos.com/creator/3.0/manual/en/scripting/ccclass.html
 * Learn more about life-cycle callbacks: https://docs.cocos.com/creator/3.0/manual/en/scripting/life-cycle-callbacks.html
 */
